package com.itcast.n9_垃圾回收;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Random;

/**
 * @program: jvm
 * @description:
 * @author: flyduck
 * @create: 2024-09-30 10:42
 **/
public class Test10_常见的引用对象_软引用应用_缓存student对象_内存不足时回收 {
    public static void main(String[] args) throws Exception {
        StudentCacheDao studentDao = new StudentCacheDao();
//        for(int i = 0;i < 100;i++){
//            studentDao.insert(new Student(i + 1, "同学" + String.valueOf(i + 1),new String(new byte[1024 * 1024 * 1])));
//        }
        for(int i = 0;;i++){
            System.out.println("=====================开始");
            Student student = studentDao.selectById(randomNums());
            System.out.println("student = " + student.getName());
            System.out.println("缓存里面数据个数 = " + studentDao.getCount());

            System.out.println("=====================结束");
            System.out.println();
        }
    }

    private static int randomNums(){
        Random random = new Random();
        return random.nextInt(100) + 1;
    }
}


class StudentCacheDao {
    private HashMap<Integer,StudentRef> studentRefs;
    private ReferenceQueue<? super Student> referenceQueue;
    private StudentDao studentDao;

    public StudentCacheDao() {
        studentRefs = new HashMap<>();
        referenceQueue = new ReferenceQueue<>();
        studentDao = new StudentDao();
    }

    public int getCount(){
        return studentRefs.size();
    }

    public void insert(Student student) throws Exception {
        studentDao.insert(student);
        System.out.println(studentDao.getCount());

    }

    public Student selectById(int id) throws Exception {
        clearCache();
        if(studentRefs.containsKey(id)){
            System.out.println("从缓存取");
            return studentRefs.get(id).get();
        }else {
            System.out.println("缓存没有，从数据库取");
            Student student = studentDao.selectById(id);
            StudentRef studentRef = new StudentRef(student, referenceQueue);
            System.out.println("放入缓存中");
            studentRefs.put(student.getId(), studentRef);
            return student;
        }
    }

    private void clearCache(){
        StudentRef studentRef = null;
        //弹出已经被回收数据的软引用对象，然后删除缓存里面的软引用对象本身
        while ((studentRef = (StudentRef) referenceQueue.poll()) != null) {
            studentRefs.remove(studentRef.getId());
        }
    }
}

class StudentRef extends SoftReference<Student> {

    private Integer id;

    public Integer getId() {
        return id;
    }

    public StudentRef(Student student, ReferenceQueue<? super Student> referenceQueue) {
        super(student, referenceQueue);
        id = student.getId();
    }
}

class StudentDao{

    private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db";
    private static final Properties PROPS = new Properties();

    static {
        PROPS.setProperty("user", "root");
        PROPS.setProperty("password", "1234");
        PROPS.setProperty("useSSL", "false");
        PROPS.setProperty("serverTimezone", "UTC");
    }

    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public int getCount() throws SQLException {
        String sql = "SELECT COUNT(*) FROM student";
        try (Connection conn = DriverManager.getConnection(DB_URL, PROPS);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            ResultSet rs = pstmt.executeQuery();
            if (rs.next()) {
                return rs.getInt(1);
            }
        }
        return 0;
    }

    public void insert(Student student) throws Exception {
        String sql = "INSERT INTO student (id, name, info) VALUES (?, ?, ?)";
        try (Connection conn = DriverManager.getConnection(DB_URL, PROPS);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, student.getId());
            pstmt.setString(2, student.getName());
            pstmt.setString(3, student.getInfo());
            pstmt.executeUpdate();
        }
    }

    public Student selectById(int id) throws Exception {
        String sql = "SELECT * FROM student WHERE id = ?";
        try (Connection conn = DriverManager.getConnection(DB_URL, PROPS);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, id);
            ResultSet rs = pstmt.executeQuery();
            if (rs.next()) {
                int studentId = rs.getInt("id");
                String name = rs.getString("name");
                String info = rs.getString("info");
                return new Student(studentId, name, info);
            }
        }
        return null;
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Student {
    private int id;
    private String name;
    private String info;
}
